"""
认证相关API
"""
from fastapi import APIRouter, Depends, HTTPException, status, Request
import asyncio
import logging
from starlette.concurrency import run_in_threadpool
from sqlalchemy.orm import Session
from datetime import datetime

from utils.database import get_db
from Models.login.models import User, LoginLog, LoginType, Role
from Models.login.schemas import UserRegister, UserLogin, LoginResponse, UserInfo, Response, DingTalkLoginCallback, RoleInfo, SelectRoleRequest
from utils import JWTUtils, DingTalkUtils, RedisUtils
from utils.login_factory import LoginFactory, LoginService

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/auth", tags=["认证"])


@router.post("/register", response_model=Response, summary="用户注册")
async def register(user_data: UserRegister, db: Session = Depends(get_db)):
    """
    用户注册接口
    """
    # 检查用户名是否已存在（在线程池执行，避免阻塞事件循环）
    existing_user = await run_in_threadpool(
        lambda: db.query(User).filter(User.username == user_data.username).first()
    )
    if existing_user:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="用户名已存在"
        )
    
    # 检查邮箱是否已存在
    if user_data.email:
        existing_email = await run_in_threadpool(
            lambda: db.query(User).filter(User.email == user_data.email).first()
        )
        if existing_email:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="邮箱已被注册"
            )
    
    # 检查手机号是否已存在
    if user_data.phone:
        existing_phone = await run_in_threadpool(
            lambda: db.query(User).filter(User.phone == user_data.phone).first()
        )
        if existing_phone:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="手机号已被注册"
            )
    
    # 创建新用户
    # 在独立线程中计算密码哈希
    password_hash = await asyncio.to_thread(JWTUtils.get_password_hash, user_data.password)
    # 确保可选字段为空字符串时转换为None
    email = user_data.email if user_data.email and user_data.email.strip() else None
    phone = user_data.phone if user_data.phone and user_data.phone.strip() else None
    nickname = user_data.nickname if user_data.nickname and user_data.nickname.strip() else None
    
    new_user = User(
        username=user_data.username,
        email=email,
        phone=phone,
        password_hash=password_hash,
        nickname=nickname or user_data.username,
        is_verified=False
    )
    
    # 将新增与提交放入线程池执行
    await run_in_threadpool(lambda: (db.add(new_user), db.commit()))
    await run_in_threadpool(lambda: db.refresh(new_user))
    
    return Response(
        code=200,
        message="注册成功",
        data={"user_id": new_user.id, "username": new_user.username}
    )


@router.post("", response_model=LoginResponse, summary="账号密码登录")
async def login(
    user_data: UserLogin,
    request: Request,
    db: Session = Depends(get_db)
):
    """
    账号密码登录接口（使用工厂模式）
    支持使用用户名、邮箱或手机号登录
    """
    try:
        # 使用工厂模式创建登录策略
        strategy = LoginFactory.create_strategy(LoginType.PASSWORD)
        login_service = LoginService(strategy)
        
        # 执行登录
        result = await login_service.login(
            request_data={
                "username": user_data.username,
                "password": user_data.password
            },
            request=request,
            db=db
        )
        
        return LoginResponse(**result)
    except HTTPException:
        # 重新抛出 HTTP 异常（如认证失败）
        raise
    except Exception as e:
        # 捕获其他未预期的异常
        logger.error(f"登录接口发生未预期错误: {e}", exc_info=True)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"登录失败: {str(e)}"
        )


@router.get("/dingtalk/login-url", summary="获取钉钉登录URL")
async def get_dingtalk_login_url():
    """
    获取钉钉登录URL
    """
    login_url = DingTalkUtils.generate_login_url()
    return Response(
        code=200,
        message="success",
        data={"login_url": login_url}
    )


@router.post("/dingtalk/callback", response_model=LoginResponse, summary="钉钉登录回调")
async def dingtalk_login_callback(
    callback_data: DingTalkLoginCallback,
    request: Request,
    db: Session = Depends(get_db)
):
    """
    钉钉登录回调接口 - OAuth 2.0流程（使用工厂模式）
    """
    # 使用工厂模式创建登录策略
    strategy = LoginFactory.create_strategy(LoginType.DINGTALK)
    login_service = LoginService(strategy)
    
    # 执行登录
    result = await login_service.login(
        request_data={"code": callback_data.code},
        request=request,
        db=db
    )
    
    return LoginResponse(**result)


@router.get("/me", response_model=UserInfo, summary="获取当前用户信息")
async def get_current_user(
    request: Request,
    db: Session = Depends(get_db)
):
    """
    获取当前登录用户信息
    需要在请求头中携带 Authorization: Bearer {token}
    """
    # 从请求头获取 token
    authorization = request.headers.get("Authorization")
    if not authorization:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="未提供认证令牌"
        )
    
    # 提取 token (格式: "Bearer <token>")
    try:
        token = authorization.split(" ")[1]
    except IndexError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="令牌格式错误"
        )
    
    # 解码令牌
    payload = JWTUtils.decode_access_token(token)
    if not payload:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="令牌无效或已过期"
        )
    
    user_id = payload.get("user_id")
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="令牌数据无效"
        )
    
    # 校验 Redis 中的登录态是否存在（支持登出/强制下线）
    token_in_redis = await RedisUtils.get_token(token)
    if not token_in_redis:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="登录状态已失效，请重新登录"
        )

    # 查找用户（包含角色关系，使用joinedload预加载role）
    def get_user_with_role():
        from sqlalchemy.orm import joinedload
        return db.query(User).options(joinedload(User.role)).filter(User.id == user_id).first()
    
    user = await run_in_threadpool(get_user_with_role)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="用户不存在"
        )
    
    # 同步检查：如果is_verified为True，但RealNameAuth表中没有记录，则自动修正
    if user.is_verified:
        def check_verification():
            from Models.login.models import RealNameAuth
            verification = db.query(RealNameAuth).filter(RealNameAuth.user_id == user_id).first()
            if not verification:
                # 认证记录不存在，但is_verified为True，需要修正
                user.is_verified = False
                db.commit()
                return False
            return True
        
        is_actually_verified = await run_in_threadpool(check_verification)
        if not is_actually_verified:
            user.is_verified = False
    
    # 构建用户信息，手动设置 role_code
    user_dict = {
        "id": user.id,
        "username": user.username,
        "email": user.email,
        "phone": user.phone,
        "nickname": user.nickname,
        "avatar": user.avatar,
        "role_id": user.role_id,
        "role_code": user.role.code if user.role else None,  # 从关联的role对象获取code
        "is_verified": user.is_verified,
        "created_at": user.created_at,
        "updated_at": user.updated_at,
        "last_login": user.last_login
    }
    
    return UserInfo(**user_dict)


@router.get("/roles", response_model=Response, summary="获取可选角色列表")
async def list_roles(db: Session = Depends(get_db)):
    roles = await run_in_threadpool(
        lambda: db.query(Role).filter(Role.is_active == True).all()
    )
    return Response(code=200, message="success", data={
        "roles": [RoleInfo.model_validate(r).model_dump() for r in roles]
    })


@router.post("/select-role", response_model=Response, summary="选择角色")
async def select_role(
    req: SelectRoleRequest,
    request: Request,
    db: Session = Depends(get_db)
):
    # 从请求头获取并校验 token
    authorization = request.headers.get("Authorization")
    if not authorization:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="未提供认证令牌"
        )
    try:
        token = authorization.split(" ")[1]
    except IndexError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="令牌格式错误"
        )

    payload = JWTUtils.decode_access_token(token)
    if not payload or not payload.get("user_id"):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="令牌无效或已过期"
        )
    user_id = payload["user_id"]

    # 校验角色是否存在且可用（在线程池中执行）
    def update_user_role():
        role = db.query(Role).filter(Role.id == req.role_id, Role.is_active == True).first()
        if not role:
            return None, "角色不存在或不可用"

        # 更新用户角色
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            return None, "用户不存在"
        user.role_id = role.id
        db.commit()
        db.refresh(user)
        return user, None
    
    user, error = await run_in_threadpool(update_user_role)
    if error:
        raise HTTPException(status_code=404, detail=error)

    # 重新签发 token（可选，携带 role_id）
    new_token = JWTUtils.create_access_token(
        data={
            "user_id": user.id,
            "username": user.username,
            "login_type": payload.get("login_type"),
            "role_id": user.role_id,
        }
    )
    # 尝试保存到 Redis（忽略失败）
    try:
        await RedisUtils.save_token(new_token, user.id, {
            "username": user.username,
            "login_type": payload.get("login_type"),
            "role_id": user.role_id,
        })
    except Exception:
        pass

    return Response(
        code=200,
        message="角色已更新",
        data={
            "access_token": new_token,
            "user_info": {
                "id": user.id,
                "username": user.username,
                "email": user.email,
                "phone": user.phone,
                "nickname": user.nickname,
                "avatar": user.avatar,
                "role_id": user.role_id,
                "is_verified": user.is_verified
            }
        }
    )

@router.post("/logout", summary="退出登录")
async def logout(request: Request):
    """移除当前 Token 的登录态"""
    authorization = request.headers.get("Authorization")
    if not authorization:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="未提供认证令牌"
        )
    try:
        token = authorization.split(" ")[1]
    except IndexError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="令牌格式错误"
        )
    await RedisUtils.delete_token(token)
    return {"code": 200, "message": "已退出登录"}

